`timescale 1ns/1ps
module tb_axi_stream_insert_header();

//---------------------------------------
// 参数定义
//---------------------------------------
parameter DATA_WD        = 32;
parameter DATA_BYTE_WD   = 4;
parameter BYTE_CNT_WD    = 2;
parameter CLK_PERIOD     = 10;
parameter TOTAL_TEST_NUM = 1000;
parameter SEED           = 12345; // 固定随机种子保证可重复性

//---------------------------------------
// 时钟与复位信号
//---------------------------------------
reg clk;
reg rst_n;
initial begin
    clk = 0;
    forever #(CLK_PERIOD/2) clk = ~clk;
end

initial begin
    rst_n = 0;
    #(CLK_PERIOD*2) rst_n = 1;
end

//---------------------------------------
// DUT实例化
//---------------------------------------
wire                    valid_in;
wire [DATA_WD-1:0]      data_in;
wire [DATA_BYTE_WD-1:0] keep_in;
wire                    last_in;
wire                    ready_in;

wire                    valid_out;
wire [DATA_WD-1:0]      data_out;
wire [DATA_BYTE_WD-1:0] keep_out;
wire                    last_out;
reg                     ready_out;

wire                    valid_insert;
wire [DATA_WD-1:0]      data_insert;
wire [DATA_BYTE_WD-1:0] keep_insert;
wire [BYTE_CNT_WD-1:0]  byte_insert_cnt;
wire                    ready_insert;

axi_stream_insert_header dut(.*);

//---------------------------------------
// 测试控制变量
//---------------------------------------
integer sent_header = 0;    // 已发送Header数
integer sent_packet = 0;    // 已发送数据包数
integer recv_count  = 0;    // 已接收数据数
integer error_count = 0;    // 错误计数器

//---------------------------------------
// 随机数据生成（Vivado兼容实现）
//---------------------------------------
reg [DATA_WD-1:0]      header_data [0:999];
reg [DATA_BYTE_WD-1:0] header_keep [0:999];
reg [BYTE_CNT_WD-1:0]  header_cnt [0:999];

reg [DATA_WD-1:0]      packet_data [0:9999];
reg [DATA_BYTE_WD-1:0] packet_keep [0:9999];
reg                    packet_last [0:9999];

initial begin
    integer i, j, burst_len, insert_cnt;
    reg [DATA_BYTE_WD-1:0] tmp_keep;
    
    // 初始化随机种子
    $display("Random seed: %0d", SEED);
    $srandom(SEED);

    // 生成Header测试数据
    for(i=0; i<TOTAL_TEST_NUM; ) begin
        // 30%概率插入Header
        if(($urandom%100) < 30 && (i<TOTAL_TEST_NUM)) begin
            insert_cnt = ($urandom%DATA_BYTE_WD) + 1;
            tmp_keep = (1 << insert_cnt) - 1; // 生成连续有效掩码
            
            // 制造10%概率的错位Header
            if(($urandom%100) < 10) begin
                tmp_keep = tmp_keep << ($urandom%DATA_BYTE_WD);
                insert_cnt = $countones(tmp_keep);
            end
            
            header_data[sent_header] = $urandom;
            header_keep[sent_header] = tmp_keep;
            header_cnt[sent_header] = insert_cnt;
            sent_header = sent_header + 1;
            i = i + 1;
        end
        
        // 生成数据包（1-16拍）
        burst_len = ($urandom%15) + 1;
        for(j=0; j<burst_len && i<TOTAL_TEST_NUM; j=j+1) begin
            packet_last[sent_packet] = (j == burst_len-1);
            
            // 20%概率生成非法keep_in
            if(!packet_last[sent_packet] && ($urandom%100) < 20) begin
                packet_keep[sent_packet] = $urandom;
            end else begin
                packet_keep[sent_packet] = packet_last[sent_packet] ? 
                    ($urandom%(2**DATA_BYTE_WD-1))+1 : {DATA_BYTE_WD{1'b1}};
            end
            
            packet_data[sent_packet] = $urandom;
            sent_packet = sent_packet + 1;
            i = i + 1;
        end
    end
    $display("Generated %0d headers and %0d data packets", sent_header, sent_packet);
end

//---------------------------------------
// 驱动逻辑
//---------------------------------------
integer hdr_ptr = 0;
integer pkt_ptr = 0;

assign valid_insert    = (hdr_ptr < sent_header);
assign data_insert     = valid_insert ? header_data[hdr_ptr] : 0;
assign keep_insert     = valid_insert ? header_keep[hdr_ptr] : 0;
assign byte_insert_cnt = valid_insert ? header_cnt[hdr_ptr] : 0;

assign valid_in = (pkt_ptr < sent_packet);
assign data_in  = valid_in ? packet_data[pkt_ptr] : 0;
assign keep_in  = valid_in ? packet_keep[pkt_ptr] : 0;
assign last_in  = valid_in ? packet_last[pkt_ptr] : 0;

// 反压信号生成（动态概率控制）
reg [1:0] backpressure_level = 0;
always @(posedge clk) begin
    if(rst_n) begin
        case(backpressure_level)
            0: ready_out <= ($urandom%100) < 90;  // 10%反压
            1: ready_out <= ($urandom%100) < 70;  // 30%反压
            2: ready_out <= ($urandom%100) < 30;  // 70%反压
            default: ready_out <= 1;
        endcase
        // 每100周期切换反压强度
        if($time%1000 == 0) backpressure_level = ($urandom%3);
    end else begin
        ready_out <= 0;
    end
end

// 指针更新
always @(posedge clk) begin
    if(!rst_n) begin
        hdr_ptr <= 0;
        pkt_ptr <= 0;
    end else begin
        if(valid_insert && ready_insert) hdr_ptr <= hdr_ptr + 1;
        if(valid_in && ready_in) pkt_ptr <= pkt_ptr + 1;
    end
end

//---------------------------------------
// 自动检查逻辑
//---------------------------------------
reg [DATA_WD-1:0]      exp_data [0:19999];
reg [DATA_BYTE_WD-1:0] exp_keep [0:19999];
reg                    exp_last [0:19999];
integer exp_wptr = 0;
integer exp_rptr = 0;

// 预期结果生成
always @(posedge clk) begin
    if(rst_n) begin
        // Header预期数据展开
        if(valid_insert && ready_insert) begin
            integer cnt = header_cnt[hdr_ptr];
            reg [DATA_WD-1:0] hdr = header_data[hdr_ptr];
            hdr = hdr << (8*(DATA_BYTE_WD - cnt)); // 左对齐处理
            
            for(integer k=0; k<cnt; k=k+1) begin
                exp_data[exp_wptr] = hdr;
                exp_keep[exp_wptr] = (1 << (DATA_BYTE_WD-1)) >> k;
                exp_last[exp_wptr] = 0;
                exp_wptr = exp_wptr + 1;
                hdr = hdr << 8;
            end
        end
        
        // 数据预期存储
        if(valid_in && ready_in) begin
            exp_data[exp_wptr] = packet_data[pkt_ptr];
            exp_keep[exp_wptr] = packet_keep[pkt_ptr];
            
            // 强制修正非last拍keep
            if(!packet_last[pkt_ptr] && (packet_keep[pkt_ptr] != {DATA_BYTE_WD{1'b1}})) begin
                exp_keep[exp_wptr] = {DATA_BYTE_WD{1'b1}};
            end
            
            exp_last[exp_wptr] = packet_last[pkt_ptr];
            exp_wptr = exp_wptr + 1;
        end
    end
end

// 实时检查器
always @(posedge clk) begin
    if(rst_n && valid_out && ready_out) begin
        // 数据检查
        if(data_out !== exp_data[exp_rptr]) begin
            $display("[%0t] DATA ERR! EXP:0x%h ACT:0x%h", $time, exp_data[exp_rptr], data_out);
            error_count = error_count + 1;
        end
        
        // keep_out检查
        if(keep_out !== exp_keep[exp_rptr]) begin
            $display("[%0t] KEEP ERR! EXP:%b ACT:%b", $time, exp_keep[exp_rptr], keep_out);
            error_count = error_count + 1;
        end
        
        // last_out检查
        if(last_out !== exp_last[exp_rptr]) begin
            $display("[%0t] LAST ERR! EXP:%b ACT:%b", $time, exp_last[exp_rptr], last_out);
            error_count = error_count + 1;
        end
        
        exp_rptr = exp_rptr + 1;
        recv_count = recv_count + 1;
    end
end

//---------------------------------------
// 仿真结束检查
//---------------------------------------
initial begin
    #((TOTAL_TEST_NUM*10)*CLK_PERIOD); // 超时保护
    
    if(error_count > 0) begin
        $display("[FAIL] Total %0d errors detected", error_count);
    end else if(exp_rptr != exp_wptr) begin
        $display("[FAIL] Data loss! Sent:%0d Recv:%0d", exp_wptr, exp_rptr);
    end else begin
        $display("[PASS] All %0d transfers verified", exp_rptr);
    end
    
    $display("Test coverage:");
    $display("- Header insertion: %0d cases", sent_header);
    $display("- Illegal keep_in:  %0d cases", sent_packet/5); // 20%概率
    $finish;
end

endmodule